import { getBaseUrl } from '@/utils/http';
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';

axios.defaults.withCredentials = true;
import qs from 'qs';

export const loginUrl =
  import.meta.env.VITE_APP_ENV === 'prod' ? 'https://sso.saash.vdyoo.com/portal/login/A361827957' : 'https://sso.saash.vdyoo.com/portal/login/A1461826738';
export interface BaseResponse<T = any> {
  res: { source_url: string; introduction: string; timestamp: number; sign: string };
  code?: number;
  msg: string;
  data: T;
}

const ErrorDic: {
  [key: number]: string;
} = {
  400: '错误的请求',
  401: '未授权，请登录',
  403: '服务器拒绝访问',
  404: '服务器未找到资源',
  408: '请求超时',
  500: '服务器内部错误',
  502: '网关错误',
  503: '服务不可用',
  504: '网关超时'
};

class ApiError extends Error {
  constructor(public code = 0, message = '') {
    super(message);
    this.name = this.constructor.name;
  }

  static createBuiltInApiError(statusCode: number) {
    const message = ErrorDic[statusCode];
    if (message) {
      return new ApiError(statusCode, message);
    } else {
      return new ApiError(statusCode, '抱歉出错了');
    }
  }
}

export enum RequestContentType {
  URL_ENCODE = 0,
  JSON = 1,
  FORM_DATA = 2,
  FORM_URLENCODED = 3
}

interface MindRequestConfig {
  withCredentials?: boolean;
  contentType?: RequestContentType;
}
const formUrlencoded = 'application/x-www-form-urlencoded';
const formDataContentType = 'multipart/form-data';
const defaultRequestConfig: AxiosRequestConfig = {
  timeout: 20000,
  headers: {}
};

function getInstanceConfig(instanceConfig: AxiosRequestConfig): AxiosRequestConfig {
  const { timeout, baseURL, headers } = instanceConfig;
  let mergedHeaders = defaultRequestConfig.headers;
  if (instanceConfig.headers) {
    mergedHeaders = Object.assign({}, defaultRequestConfig.headers, instanceConfig.headers);
  }
  return Object.assign({}, defaultRequestConfig, { timeout, baseURL, mergedHeaders });
}

type InterceptorFulfill<V> = (value: V) => V | Promise<V>;
type InterceptorReject = (error: any) => any;

export class AjaxRequest {
  private _axiosInstanceConfig: AxiosRequestConfig;
  private _ajax: AxiosInstance;
  private _concurrencyMap: Map<string, Promise<AxiosResponse<any>>>;

  constructor(instanceConfig: AxiosRequestConfig = {}) {
    this._concurrencyMap = new Map();

    this._axiosInstanceConfig = getInstanceConfig(instanceConfig);
    this._ajax = axios.create(this._axiosInstanceConfig);

    this._ajax.interceptors.response.use(
      (res: any) => {
        try {
          if (res && res.status === 200) {
            if (res.data) {
              const { code } = res.data;
              if (code === 1005) {
                // window.location.replace(loginUrl + '?param=' + getBaseUrl(''));
              }
              return res;
            } else {
              throw new ApiError(200, '接口返回数据为空');
            }
          } else if (res && res.status === 401) {
            // window.location.replace(loginUrl + '?param=' + getBaseUrl(''));
          } else {
            throw new ApiError(0, '抱歉出错了');
          }
        } catch (err) {
          throw err;
        }
      },
      (error) => {
        let errorInstance = null;
        if (error.response) {
          errorInstance = ApiError.createBuiltInApiError(error.response.status);
        } else {
          errorInstance = new ApiError(0, '抱歉出错了');
        }

        // throw error
        return Promise.reject(errorInstance);
      }
    );
  }

  registerRequestInterceptor(fulfilled: InterceptorFulfill<AxiosRequestConfig>, rejected: InterceptorReject) {
    if (fulfilled) {
      this._ajax.interceptors.request.use(fulfilled, rejected);
    }
  }

  registerResponseInterceptor<V>(fulfilled: InterceptorFulfill<AxiosResponse<V>>, rejected: InterceptorReject) {
    if (fulfilled) {
      this._ajax.interceptors.response.use(fulfilled, rejected);
    }
  }

  changeAxiosHeaderCommon(headerText: string, value: string): void {
    if (headerText !== '') {
      axios.defaults.headers.common[headerText] = value;
    }
  }

  async noRepeatGet<V extends BaseResponse>(url: string, params: any, { withCredentials = true }: MindRequestConfig = {}): Promise<AxiosResponse<V>> {
    const resultParams = params || {};
    resultParams.ts = +new Date();
    const key = qs.stringify({
      url,
      params
    });
    if (this._concurrencyMap.has(key)) {
      const result = (await this._concurrencyMap.get(key)) as AxiosResponse<V>;
      if (result.data.code !== 0) {
        throw new ApiError(result.data.code, result.data.msg);
      } else {
        return result;
      }
    } else {
      const pro = this._ajax.get<V, AxiosResponse<V>>(url, {
        params: resultParams,
        withCredentials
      });
      this._concurrencyMap.set(key, pro);
      const result = await pro;
      this._concurrencyMap.delete(key);
      return result;
    }
  }

  async get<V extends BaseResponse>(url: string, params: any, { withCredentials = true }: MindRequestConfig = {}): Promise<AxiosResponse<V>> {
    const resultParams = params || {};
    const pro = this._ajax.get<V, AxiosResponse<V>>(url, {
      params: resultParams,
      withCredentials
    });
    const result = await pro;
    return result;
  }

  async post<V extends BaseResponse>(
    url: string,
    params: any,
    { withCredentials = true, contentType = RequestContentType.JSON }: MindRequestConfig = {}
  ): Promise<AxiosResponse<V>> {
    let resultParams = params;
    if (contentType === RequestContentType.URL_ENCODE) {
      resultParams = qs.stringify(resultParams);
    }
    const httpHeader: any = {};
    if (contentType === RequestContentType.FORM_DATA) {
      httpHeader['Content-Type'] = formDataContentType;
    }
    if (contentType === RequestContentType.FORM_URLENCODED) {
      httpHeader['Content-Type'] = formUrlencoded;
      resultParams = qs.stringify(params);
    }
    const result = await this._ajax.post<V, AxiosResponse<V>>(url, resultParams, {
      withCredentials,
      headers: httpHeader
    });
    return result;
  }
}

export default new AjaxRequest({
  baseURL: import.meta.env.VITE_API_HAICHUAN_DOMAIN
});
